/********************************************************************
 * Inspiré de http://ramsrigoutham.com/2012/11/22/panorama-image-stitching-in-opencv/
 *
 *
 *
 *
 *
 *
 *
 *
 ********************************************************************/
#include <QCoreApplication>
#include <QElapsedTimer>

#include <stdio.h>
#include <iostream>

#include "opencv2/core/core.hpp"
#include "opencv2/features2d/features2d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/nonfree/nonfree.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void readme();



/** @function main */
int main( int argc, char** argv )
{
    QElapsedTimer timer;

    if( argc <= 3 )
    {
        readme();
        return -1;
    }

    // Load the images
    Mat image1= imread( argv[2] );
    Mat image2= imread( argv[1] );
    Mat gray_image1;
    Mat gray_image2;

    timer.start();
    // Convert to Grayscale
    cvtColor( image1, gray_image1, CV_RGB2GRAY );
    cvtColor( image2, gray_image2, CV_RGB2GRAY );

    //imshow("first image",image2);
    //imshow("second image",image1);

    if( !gray_image1.data || !gray_image2.data )
    {
        std::cout<< " --(!) Error reading images " << std::endl;
        return -1;
    }

    //-- Step 1: Detect the keypoints using SURF Detector
    int minHessian = 400;
    cout << "Using Feature Detector : " << argv[3] <<endl;
    Ptr<FeatureDetector> featureDetector = FeatureDetector::create(argv[3]);

    vector< KeyPoint > keypoints_object, keypoints_scene;

    featureDetector->detect(gray_image1, keypoints_object);
    featureDetector->detect(gray_image2, keypoints_scene);

    cout << "# of keypoints in image1: " <<  keypoints_object.size() <<endl;
    cout << "# of keypoints in image2: " << keypoints_scene.size() <<endl;

    //-- Step 2: Calculate descriptors (feature vectors)
    Ptr<DescriptorExtractor> descriptorExtractor = DescriptorExtractor::create(argv[4]);
    SurfDescriptorExtractor extractor;

    Mat descriptors_object, descriptors_scene;

    descriptorExtractor->compute( gray_image1, keypoints_object, descriptors_object );
    descriptorExtractor->compute( gray_image2, keypoints_scene, descriptors_scene );

    cout << "Using Feature Extractor : " << argv[4] <<endl;


    //-- Step 2.5 BUG dans open CV
    if(descriptors_object.type()!=CV_32F) {
        descriptors_object.convertTo(descriptors_object, CV_32F);
    }

    if(descriptors_scene.type()!=CV_32F) {
        descriptors_scene.convertTo(descriptors_scene, CV_32F);
    }

    //-- Step 3: Matching descriptor vectors using FLANN matcher
    Ptr<DescriptorMatcher> descriptorMatcher = DescriptorMatcher::create(argv[5]);

    //FlannBasedMatcher matcher;
    vector< DMatch > matches;
    descriptorMatcher->match( descriptors_object, descriptors_scene, matches );

    double max_dist = 0; double min_dist = 100;

    //-- Quick calculation of max and min distances between keypoints
    for( int i = 0; i < descriptors_object.rows; i++ )
    { double dist = matches[i].distance;
        if( dist < min_dist ) min_dist = dist;
        if( dist > max_dist ) max_dist = dist;
    }

    printf("-- Max dist : %f \n", max_dist );
    printf("-- Min dist : %f \n", min_dist );

    //-- Use only "good" matches (i.e. whose distance is less than 3*min_dist )
    std::vector< DMatch > good_matches;

    for( int i = 0; i < descriptors_object.rows; i++ )
    { if( matches[i].distance < 3*min_dist )
        { good_matches.push_back( matches[i]); }
    }
    std::vector< Point2f > obj;
    std::vector< Point2f > scene;

    for( int i = 0; i < good_matches.size(); i++ )
    {
        //-- Get the keypoints from the good matches
        obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt );
        scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt );
    }

    // Find the Homography Matrix
    Mat H = findHomography( obj, scene, CV_RANSAC );
    // Use the Homography Matrix to warp the images
    cv::Mat result;
    warpPerspective(image1,result,H,cv::Size(image1.cols+image2.cols,image1.rows));
    cv::Mat half(result,cv::Rect(0,0,image2.cols,image2.rows));


    image2.copyTo(half);
    cout << "The operation took" << timer.elapsed() << "milliseconds" << endl;
    cout << "Matches : " << good_matches.size() << endl;


    imshow( "Result", result );

    waitKey(0);
    return 0;
}

/** @function readme */
void readme()
{ std::cout << " Usage: Panorama < img1 > < img2 >" << std::endl; }
